
package GeneticAlgorithm;

import Individual.FitnessFunctions;
import Individual.Individual;
import Loader.BufferedData;
import Loader.Pair;
import Settings.Settings;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;

/**
 *
 * @author krzysiek
 */
public class StatisticFunctions {

    Individual[] individuals;
    FitnessFunctions fitnessFunctions;
    BufferedData bufferedData;

    public StatisticFunctions( Individual[] individuals,
                               FitnessFunctions fitnessFunctions,
                               BufferedData bufferedData
                             ) {
        this.individuals = individuals;
        this.fitnessFunctions = fitnessFunctions;
        this.bufferedData = bufferedData;
    }

    protected float averageFitness(boolean learnData) {

        float result = 0;

        for(int i = 0; i < individuals.length; ++i) {

            result += fitnessFunctions.fitness(individuals[i], learnData);

        }

        return result/individuals.length;
    }

    protected float bestFitness(boolean learnData) {

        float result = 0;
        float fitTemp = 0;

        for(int i = 0; i < individuals.length; ++i) {

            fitTemp = fitnessFunctions.fitness(individuals[i], learnData);
            if(result < fitTemp)
                result = fitTemp;
        }

        return result;
    }

    protected PairIndividualFitness getBestIndividual() {

        if(individuals.length == 0) {

            System.out.println("Brak osobnikow!");
            return null;

        }

        Individual individual = individuals[0];
        float bestFit = 0;
        float fitTemp = 0;
        
        //find best individual
        for(int i = 0; i < individuals.length; ++i) {

            fitTemp = fitnessFunctions.fitness(individuals[i], true);
            if(bestFit < fitTemp) {

                individual = individuals[i];
                bestFit = fitTemp;

            }
        }

        return new PairIndividualFitness(individual, bestFit);

    }

    protected PairIndividualFitness getWorstIndividual() {

        if(individuals.length == 0) {

            System.out.println("Brak osobnikow!");
            return null;

        }

        Individual individual = individuals[0];
        float worstFit = 0;
        float fitTemp = 0;

        //find best individual
        for(int i = 0; i < individuals.length; ++i) {

            fitTemp = fitnessFunctions.fitness(individuals[i], true);
            if(worstFit > fitTemp) {

                individual = individuals[i];
                worstFit = fitTemp;

            }
        }

        return new PairIndividualFitness(individual, worstFit);

    }

    protected PairIndividualFitness getAvgIndividual() {

        if(individuals.length == 0) {

            System.out.println("Brak osobnikow!");
            return null;

        }

        //Individual individual = individuals[0];
        int numIndividual = 0;
        float avgFit = 0;
        float dist=Float.MAX_VALUE;
        float[] fitsInds = new float[individuals.length];

        //find average individual
        for(int i = 0; i < individuals.length; ++i) {
            fitsInds[i] = fitnessFunctions.fitness(individuals[i], true);
            avgFit += fitsInds[i];
        }
        avgFit /= individuals.length;

        for(int i = 0; i < individuals.length; ++i) {
            float distTemp = Math.abs(fitsInds[i] - avgFit);
            if(distTemp < dist) {

                numIndividual = i;
                dist = distTemp;

            }
        }

        return new PairIndividualFitness( individuals[numIndividual],
                                          fitsInds[numIndividual] );

    }

    protected void printCentroidsBestIndividual() {

        if(individuals.length == 0) {

            System.out.println("Brak osobnikow!");
            return;

        }

        Individual individual = getBestIndividual().individual;

        System.out.println("Centroids best individual: ");

        //print its centroids
        for(int c = 0; c < individual.numOfClusters; ++c) {

            //features for cluster
            for(int f = 0; f < individual.numOfFeatures; ++f) {

                System.out.print( String.format("%2.7f, ", 
                                  individual.chromosomeVal.get(c).get(f))
                                );

            }

            System.out.println();

        }

    }

    protected void printCountersAssigned(boolean learn) {

        if(individuals.length == 0) {

            System.out.println("Brak osobnikow!");
            return;

        }

        Individual individual = getBestIndividual().individual;
        int[][] countersClusters = new int[individual.numOfClusters][3];

        Iterator<Pair> it;
        if(learn)
            it = bufferedData.dataLearn.iterator();
        else
            it = bufferedData.dataTest.iterator();

        while( it.hasNext() ) {

            Pair pair = it.next();
            float fitMin = Float.MAX_VALUE;
            int clusterSelected = 0;

            for(int c = 0; c < individual.numOfClusters; ++c) {

                float fitTemp = 0;

                //features for cluster
                for(int f = 0; f < individual.numOfFeatures; ++f) {

                    fitTemp += Math.pow(individual.chromosomeVal.get(c).get(f) -
                                        pair.features[f], 2);

                }

                if( fitTemp < fitMin ) {

                    fitMin = fitTemp;
                    clusterSelected = c;
                }

            }

            countersClusters[clusterSelected][pair.noClass-1]++;

        }

        if(learn)
            System.out.println("Counters of clusters for learn data: ");
        else
            System.out.println("Counters of clusters for test data: ");

        for(int i = 0; i < individual.numOfClusters; ++i)
            System.out.println("Cluster["+(i+1)+"]: "+
                                countersClusters[i][0]+", "+
                                countersClusters[i][1]+", "+
                                countersClusters[i][2]);
        
    }

    protected void printPrecisionRecall(boolean learn, 
                                        double beta,
                                        Individual individual) {
        if(individuals.length == 0) {
            System.out.println("Brak osobnikow!");
            return;
        }

        if(individual == null)
            individual = getBestIndividual().individual;
        
        int[][] countersClusters = new int[Settings.CHROMOSOME_NUMBER_OF_CLUSTERS]
                                          [Settings.NUMBER_OF_CLASS];

        Iterator<Pair> it;
        if(learn)
            it = bufferedData.dataLearn.iterator();
        else
            it = bufferedData.dataTest.iterator();

        //count how many records belong to classes in clusters for best individ.
        while( it.hasNext() ) {

            Pair pair = it.next();
            float fitMin = Float.MAX_VALUE;
            int clusterSelected = 0;

            for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {

                float fitTemp = 0;

                //features for cluster
                for(int f = 0; f < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f){

                    fitTemp += Math.pow(individual.chromosomeVal.get(c).get(f) -
                                        pair.features[f], 2);

                }

                if( fitTemp < fitMin ) {

                    fitMin = fitTemp;
                    clusterSelected = c;
                }

            }

            countersClusters[clusterSelected][pair.noClass-1]++;

        }

        //compute TP(m=0), FP(m=1), FN(m=2), TN(m=3) measures for each class
        double[][] measures = new double[Settings.NUMBER_OF_CLASS][4];
        for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
            for(int m = 0; m < 4; ++m)
                measures[i][m] = 0.0f;

        for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {

            //find what class is represented by cluster c
            int noClassInCluster = -1;
            int maxRecordsInClass = 0;
            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if(maxRecordsInClass < countersClusters[c][i]) {
                    maxRecordsInClass = countersClusters[c][i];
                    noClassInCluster = i;
                }

            //cluster is empty
            if(noClassInCluster == -1)
                continue;

            //compute measure
            boolean errorNoClass = false;
            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if( maxRecordsInClass == countersClusters[c][i] &&
                    i != noClassInCluster
                  ) {

                    errorNoClass = true;
                    measures[i][2] += countersClusters[c][i];//FN
                    
                }
            
            if(errorNoClass) {

                measures[noClassInCluster][2] += maxRecordsInClass;//FN
                for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                    if( i != noClassInCluster &&
                        countersClusters[c][i] < maxRecordsInClass
                      )
                        measures[i][3] += maxRecordsInClass;//TN

            } else {

                measures[noClassInCluster][0] += maxRecordsInClass;//TP
                for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                    if(i != noClassInCluster)
                        measures[i][3] += maxRecordsInClass;//TN
                
            }

            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if( countersClusters[c][i] < maxRecordsInClass &&
                    countersClusters[c][i] > 0
                  ) {

                  measures[i][2] += countersClusters[c][i];//FN
                  measures[noClassInCluster][1] += countersClusters[c][i];//FP
                  
                  for(int j = 0; j < Settings.NUMBER_OF_CLASS; ++j)
                      if(j != i && j != noClassInCluster)
                          measures[j][3] += countersClusters[c][i];//TN
                }
        }

        //check sum measures in each class if they're equal
        double sumMeasures = measures[0][0] +
                             measures[0][1] +
                             measures[0][2] +
                             measures[0][3];
        double sumMeasuresTemp;
        for(int i = 1; i < Settings.NUMBER_OF_CLASS; ++i) {
            sumMeasuresTemp = measures[i][0] +
                              measures[i][1] +
                              measures[i][2] +
                              measures[i][3];
            if(sumMeasuresTemp != sumMeasures) {
                System.out.println(String.format("Error in compute measures! " +
                                                 "In class %d: " +
                                                 "Sum measures=%.5f & " +
                                                 "Sum measures temp=%.5f",(i+1),
                                                 sumMeasures, sumMeasuresTemp));
                return;
            }
        }

        //compute rates for each class
        double precision;
        double recall;
        double tnr; //True Negative Rate
        double accuracy;
        double f;
        double fB;
        beta = Math.pow(beta, 2);
        for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i) {
            precision = measures[i][0] / (measures[i][0] + measures[i][1]);
            recall = measures[i][0] / (measures[i][0] + measures[i][2]);
            tnr = measures[i][3] / (measures[i][3] + measures[i][1]);
            accuracy = (measures[i][0] + measures[i][3]) / sumMeasures;
            f = 2 * ( (precision*recall) / (precision+recall) );
            fB = (1+beta) * ( (precision*recall) / (beta*precision+recall) );
            String str = String.format("Class %d: " +
                    "prec(%.5f), recall(%.5f), TNR(%.5f), " +
                    "Accur(%.5f), F(%.5f), FB(%.5f)",
                    i, precision, recall, tnr, accuracy, f, fB);
            System.out.println(str);
        }

    }

    protected float getAverageAccuracy(boolean learn,
                                        Individual individual) {
        if(individuals.length == 0) {
            System.out.println("Brak osobnikow!");
            return 0.0f;
        }

        if(individual == null)
            individual = getBestIndividual().individual;

        int[][] countersClusters = new int[Settings.CHROMOSOME_NUMBER_OF_CLUSTERS]
                                          [Settings.NUMBER_OF_CLASS];

        Iterator<Pair> it;
        if(learn)
            it = bufferedData.dataLearn.iterator();
        else
            it = bufferedData.dataTest.iterator();

        //count how many records belong to classes in clusters for best individ.
        while( it.hasNext() ) {

            Pair pair = it.next();
            float fitMin = Float.MAX_VALUE;
            int clusterSelected = 0;

            for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {

                float fitTemp = 0;

                //features for cluster
                for(int f = 0; f < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f){

                    fitTemp += Math.pow(individual.chromosomeVal.get(c).get(f) -
                                        pair.features[f], 2);

                }

                if( fitTemp < fitMin ) {

                    fitMin = fitTemp;
                    clusterSelected = c;
                }

            }

            countersClusters[clusterSelected][pair.noClass-1]++;

        }

        //compute TP(m=0), FP(m=1), FN(m=2), TN(m=3) measures for each class
        double[][] measures = new double[Settings.NUMBER_OF_CLASS][4];
        for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
            for(int m = 0; m < 4; ++m)
                measures[i][m] = 0.0f;

        for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {

            //find what class is represented by cluster c
            int noClassInCluster = -1;
            int maxRecordsInClass = 0;
            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if(maxRecordsInClass < countersClusters[c][i]) {
                    maxRecordsInClass = countersClusters[c][i];
                    noClassInCluster = i;
                }

            //cluster is empty
            if(noClassInCluster == -1)
                continue;

            //compute measure
            boolean errorNoClass = false;
            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if( maxRecordsInClass == countersClusters[c][i] &&
                    i != noClassInCluster
                  ) {

                    errorNoClass = true;
                    measures[i][2] += countersClusters[c][i];//FN

                }

            if(errorNoClass) {

                measures[noClassInCluster][2] += maxRecordsInClass;//FN
                for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                    if( i != noClassInCluster &&
                        countersClusters[c][i] < maxRecordsInClass
                      )
                        measures[i][3] += maxRecordsInClass;//TN

            } else {

                measures[noClassInCluster][0] += maxRecordsInClass;//TP
                for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                    if(i != noClassInCluster)
                        measures[i][3] += maxRecordsInClass;//TN

            }

            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if( countersClusters[c][i] < maxRecordsInClass &&
                    countersClusters[c][i] > 0
                  ) {

                  measures[i][2] += countersClusters[c][i];//FN
                  measures[noClassInCluster][1] += countersClusters[c][i];//FP

                  for(int j = 0; j < Settings.NUMBER_OF_CLASS; ++j)
                      if(j != i && j != noClassInCluster)
                          measures[j][3] += countersClusters[c][i];//TN
                }
        }

        //check sum measures in each class if they're equal
        double sumMeasures = measures[0][0] +
                             measures[0][1] +
                             measures[0][2] +
                             measures[0][3];
        double sumMeasuresTemp;
        for(int i = 1; i < Settings.NUMBER_OF_CLASS; ++i) {
            sumMeasuresTemp = measures[i][0] +
                              measures[i][1] +
                              measures[i][2] +
                              measures[i][3];
            if(sumMeasuresTemp != sumMeasures) {
                System.out.println(String.format("Error in compute measures! " +
                                                 "In class %d: " +
                                                 "Sum measures=%.5f & " +
                                                 "Sum measures temp=%.5f",(i+1),
                                                 sumMeasures, sumMeasuresTemp));
                return 0.0f;
            }
        }

        //compute rate for each class
        double accuracy = 0.0f;
        for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i) {
            accuracy += (measures[i][0] + measures[i][3]) / sumMeasures;
        }
        return (float) (accuracy / Settings.NUMBER_OF_CLASS);

    }

    protected float[] getAverageAccuracyAndF( boolean learn,
                                              Individual individual) {
        if(individuals.length == 0) {
            System.out.println("Brak osobnikow!");
            return new float[] {0.0f, 0.0f};
        }

        if(individual == null)
            individual = getBestIndividual().individual;

        int[][] countersClusters = new int[Settings.CHROMOSOME_NUMBER_OF_CLUSTERS]
                                          [Settings.NUMBER_OF_CLASS];

        Iterator<Pair> it;
        if(learn)
            it = bufferedData.dataLearn.iterator();
        else
            it = bufferedData.dataTest.iterator();

        //count how many records belong to classes in clusters for best individ.
        while( it.hasNext() ) {

            Pair pair = it.next();
            float fitMin = Float.MAX_VALUE;
            int clusterSelected = 0;

            for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {

                float fitTemp = 0;

                //features for cluster
                for(int f = 0; f < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f){

                    fitTemp += Math.pow(individual.chromosomeVal.get(c).get(f) -
                                        pair.features[f], 2);

                }

                if( fitTemp < fitMin ) {

                    fitMin = fitTemp;
                    clusterSelected = c;
                }

            }

            countersClusters[clusterSelected][pair.noClass-1]++;

        }

        //compute TP(m=0), FP(m=1), FN(m=2), TN(m=3) measures for each class
        double[][] measures = new double[Settings.NUMBER_OF_CLASS][4];
        for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
            for(int m = 0; m < 4; ++m)
                measures[i][m] = 0.0f;

        for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {

            //find what class is represented by cluster c
            int noClassInCluster = -1;
            int maxRecordsInClass = 0;
            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if(maxRecordsInClass < countersClusters[c][i]) {
                    maxRecordsInClass = countersClusters[c][i];
                    noClassInCluster = i;
                }

            //cluster is empty
            if(noClassInCluster == -1)
                continue;

            //compute measure
            boolean errorNoClass = false;
            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if( maxRecordsInClass == countersClusters[c][i] &&
                    i != noClassInCluster
                  ) {

                    errorNoClass = true;
                    measures[i][2] += countersClusters[c][i];//FN

                }

            if(errorNoClass) {

                measures[noClassInCluster][2] += maxRecordsInClass;//FN
                for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                    if( i != noClassInCluster &&
                        countersClusters[c][i] < maxRecordsInClass
                      )
                        measures[i][3] += maxRecordsInClass;//TN

            } else {

                measures[noClassInCluster][0] += maxRecordsInClass;//TP
                for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                    if(i != noClassInCluster)
                        measures[i][3] += maxRecordsInClass;//TN

            }

            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if( countersClusters[c][i] < maxRecordsInClass &&
                    countersClusters[c][i] > 0
                  ) {

                  measures[i][2] += countersClusters[c][i];//FN
                  measures[noClassInCluster][1] += countersClusters[c][i];//FP

                  for(int j = 0; j < Settings.NUMBER_OF_CLASS; ++j)
                      if(j != i && j != noClassInCluster)
                          measures[j][3] += countersClusters[c][i];//TN
                }
        }

        //check sum measures in each class if they're equal
        double sumMeasures = measures[0][0] +
                             measures[0][1] +
                             measures[0][2] +
                             measures[0][3];
        double sumMeasuresTemp;
        for(int i = 1; i < Settings.NUMBER_OF_CLASS; ++i) {
            sumMeasuresTemp = measures[i][0] +
                              measures[i][1] +
                              measures[i][2] +
                              measures[i][3];
            if(sumMeasuresTemp != sumMeasures) {
                System.out.println(String.format("Error in compute measures! " +
                                                 "In class %d: " +
                                                 "Sum measures=%.5f & " +
                                                 "Sum measures temp=%.5f",(i+1),
                                                 sumMeasures, sumMeasuresTemp));
                return new float[] {0.0f, 0.0f};
            }
        }

        //compute rate for each class
        double accuracy = 0.0f;
        double f = 0.0f;
        double precision;
        double recall;
        for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i) {
            if(measures[i][0] > 0) {
                precision = measures[i][0] / (measures[i][0] + measures[i][1]);
                recall = measures[i][0] / (measures[i][0] + measures[i][2]);
            } else {
                precision = 0;
                recall = 0;
            }
//System.out.println(String.format("Class: %d Precision: %.5f recall: %.5f [ TP: %.5f, FP: %.5f, FN %.5f, TN %.5f",(i+1),precision, recall, measures[i][0], measures[i][1], measures[i][2], measures[i][3]));
            if(precision != 0 || recall != 0)
                f += 2 * ( (precision*recall) / (precision+recall) );

            accuracy += (measures[i][0] + measures[i][3]) / sumMeasures;
        }
        return new float[] { (float) (accuracy / Settings.NUMBER_OF_CLASS),
                             (float) (f / Settings.NUMBER_OF_CLASS)};

    }

    protected void paintRecordsAndCentres( Individual individual,
                                           String fileName
                                         ) throws IOException {
        if(fileName == null || fileName.length()<1) {
            System.out.println("Zła nazwa pliku do zapisu obrazka!");
            return;
        }

        if(individuals.length == 0) {
            System.out.println("Brak osobnikow!");
            return;
        }

        boolean WRITE_TEXT = false;

        if(individual == null)
            individual = getBestIndividual().individual;

        int[][] countersClusters = new int[Settings.CHROMOSOME_NUMBER_OF_CLUSTERS]
                                          [Settings.NUMBER_OF_CLASS];
        int[][] assignedClassToCluster = new int[Settings.CHROMOSOME_NUMBER_OF_CLUSTERS][2];

        Iterator<Pair> it;
//        if(learn)
//            it = bufferedData.dataLearn.iterator();
//        else
//            it = bufferedData.dataTest.iterator();
        it = bufferedData.data.iterator();
        //count how many records belong to classes in clusters for best individ.
        while( it.hasNext() ) {

            Pair pair = it.next();
            float fitMin = Float.MAX_VALUE;
            int clusterSelected = 0;

            for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {

                float fitTemp = 0;

                //features for cluster
                for(int f = 0; f < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f){

                    fitTemp += Math.pow(individual.chromosomeVal.get(c).get(f) -
                                        pair.features[f], 2);

                }

                if( fitTemp < fitMin ) {

                    fitMin = fitTemp;
                    clusterSelected = c;
                }

            }

            countersClusters[clusterSelected][pair.noClass-1]++;
        }

        for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {

            //find what class is represented by cluster c
            int noClassInCluster = -1;
            int maxRecordsInClass = 0;
            boolean isRedundancy = false;
            for(int i = 0; i < Settings.NUMBER_OF_CLASS; ++i)
                if(maxRecordsInClass < countersClusters[c][i]) {
                    maxRecordsInClass = countersClusters[c][i];
                    noClassInCluster = i;
                    isRedundancy = false;
                } else
                    isRedundancy = maxRecordsInClass == countersClusters[c][i];

            if( isRedundancy )
                noClassInCluster = -1;

            assignedClassToCluster[c][0] = noClassInCluster;
            assignedClassToCluster[c][1] = maxRecordsInClass;

        }

        int m = individual.maxValue;
        int sizePix = 3;
        int sizeSetImg = sizePix* m* (Settings.CHROMOSOME_NUMBER_OF_FEATURES-2);
        
        BufferedImage img = new BufferedImage( sizeSetImg,
                                               sizeSetImg,
                                               BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        g2.setPaint(Color.WHITE);
        g2.fill(new Rectangle(0, 0, sizeSetImg, sizeSetImg));

        g2.setPaint(Color.BLACK);
        for(int f1 = 0; f1+1 < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f1)
            for(int f2 = f1+1; f2 < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f2)
                g2.draw(new Rectangle((f2-1)*m, f1*m, m, m));

        int countFile = (Settings.CHROMOSOME_NUMBER_OF_FEATURES *
                         Settings.CHROMOSOME_NUMBER_OF_FEATURES -
                         Settings.CHROMOSOME_NUMBER_OF_FEATURES) / 2;

        File[] files;
        BufferedWriter[] outs = null;
        if(WRITE_TEXT) {
            files = new File[countFile];
            outs = new BufferedWriter[countFile];
            for(int i = 0; i < countFile; ++i) {
                files[i] = new File(fileName+"_data_"+i+".txt");
                outs[i] = new BufferedWriter(new FileWriter(files[i]));
                outs[i].write("Records:");
                outs[i].newLine();
            }
        }
//        if(learn)
//            it = bufferedData.dataLearn.iterator();
//        else
//            it = bufferedData.dataTest.iterator();
        it = bufferedData.data.iterator();
        Color[] colors = new Color[] {Color.BLUE, Color.CYAN, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.RED};
        int pos;
        
        //assign recoreds to class
        while( it.hasNext() ) {

            Pair pair = it.next();
            float fitMin = Float.MAX_VALUE;
            int clusterSelected = 0;

            for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {

                float fitTemp = 0;

                //features for cluster
                for(int f = 0; f < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f){

                    fitTemp += Math.pow(individual.chromosomeVal.get(c).get(f) -
                                        pair.features[f], 2);

                }

                if( fitTemp < fitMin ) {

                    fitMin = fitTemp;
                    clusterSelected = c;
                }

            }

//            if(assignedClassToCluster[clusterSelected][0] == pair.noClass-1) {
//                switch(pair.noClass-1) {
//                    case 0: g2.setPaint(Color.BLUE); break;
//                    case 1: g2.setPaint(Color.GREEN); break;
//                    case 2: g2.setPaint(Color.RED); break;
//                    default: g2.setPaint(Color.CYAN); break;
//                }
//            } else {
//                g2.setPaint(Color.BLACK);
//            }

            g2.setPaint(colors[clusterSelected]);

            for(int f1 = 0; f1+1 < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f1)
                for(int f2 = f1+1; f2 < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f2) {

                    int x = (int)((pair.features[f2]-bufferedData.minValues[f2]) /
                                                   bufferedData.granularity[f2]);
                    int y = (int)((pair.features[f1]-bufferedData.minValues[f1]) /
                                                   bufferedData.granularity[f1]);

                    g2.fill(new Rectangle( (f2-1)*m+x,
                                           f1*m+y,
                                           sizePix, sizePix));
                    if(WRITE_TEXT) {
                        if(f1 == 0)
                            pos = f1 + f2 - 1;
                        else
                            pos = f1 + f2;
                        outs[pos].write(pair.features[f2]+" "+pair.features[f1]+" "+clusterSelected+" "+assignedClassToCluster[clusterSelected][0]+" "+(pair.noClass-1));
                        outs[pos].newLine();
                    }
                }


        } //end while

        //g2.setPaint(Color.MAGENTA);
        if(WRITE_TEXT) {
            for(int i = 0; i < countFile; ++i) {
                outs[i].write("Centers:");
                outs[i].newLine();
            }
        }
        
        for(int f1 = 0; f1+1 < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f1)
            for(int f2 = f1+1; f2 < Settings.CHROMOSOME_NUMBER_OF_FEATURES; ++f2)
                for(int c = 0; c < Settings.CHROMOSOME_NUMBER_OF_CLUSTERS; ++c) {
                    
                    int x = (int)individual.getValue(c, f2);
                    int y = (int)individual.getValue(c, f1);


                    g2.setPaint(colors[c]);

                    
                    g2.fill(new Rectangle( (f2-1)*m+x,
                                           f1*m+y,
                                           sizePix+2, sizePix+2));
                    if(WRITE_TEXT) {
                        if(f1 == 0)
                            pos = f1 + f2 - 1;
                        else
                            pos = f1 + f2;
                        outs[pos].write(individual.chromosomeVal.get(c).get(f2)+" "+individual.chromosomeVal.get(c).get(f1));
                        outs[pos].newLine();
                    }
                }

        if(WRITE_TEXT) {
            for(int i = 0; i < countFile; ++i)
                outs[i].close();
        }

        try {
            File outputfile = new File(fileName+".png");
            ImageIO.write(img, "png", outputfile);
        } catch (Exception e) {
            System.out.println("Błąd zapisu obrazka!");
        }

    }

}
